/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <string>
#include "securec.h"
#include "napi/native_api.h"
#include "tar.h"
#include "hilog/log.h"
constexpr char *LOG_MYTAG = "TAR_TAG";

static void NAPI_ASSERT(napi_env env, size_t assertion, const char *msg)
{
    if (!(assertion)) {
        napi_throw_error(env, nullptr, "assertion failed: ");
        return;
    }
}

static void NAPI_CALL(napi_env env, size_t theCall)
{
    if ((theCall) != napi_ok) {
        const napi_extended_error_info *errorInfo = nullptr;
        napi_get_last_error_info((env), &errorInfo);
        bool isPending = false;
        napi_is_exception_pending((env), &isPending);
        if (!isPending && errorInfo != nullptr) {
            const char *errorMessage =
                errorInfo->error_message != nullptr ? errorInfo->error_message : "empty error message";
            napi_throw_error((env), nullptr, errorMessage);
        }
        return;
    }
}
    
#define MAX_COMMEND_LENGTH 256
const int MAX_STRING_LENGTH = 128;

class OHOStar {
public:
    OHOStar() {}

    ~OHOStar()
    {
        FreeTar();
    }

    static OHOStar& GetInstance()
    {
        static OHOStar* instance;  // 单例
        if (instance == nullptr) {
            instance = new OHOStar();
        }
        return *instance;
    }

    static OHOStar& FreeInstance()
    {
        OHOStar *instance = &GetInstance(); // 拿出单例
        if (instance == nullptr) {
            delete instance;    // 释放单例
        }
        return *instance;
    }

    void setTarRootPath(std::string Path)
    {
        rootPath = Path;
    }

    std::string getTarRootPath ()
    {
        return rootPath;
    }

    void addTarpath(std::string path)
    {
        // 上层不能保证不会重复，需要自己重新去检验
        std::vector < std::string >::iterator it;   // 声明一个迭代器，来访问vector容器，作用：遍历或者指向vector容器的元素
        for (it = TarPath.begin(); it != TarPath.end(); it++) {
            if (path == *it) {
                // 如果存在删除原来的
                it = TarPath.erase(it);
            }
        }
        // 重新添加
        TarPath.push_back(path);
        mark = true;    // 发生修改进行标记
    }

    void delTarpath(std::string path)
    {
        std::vector < std::string >::iterator it;   // 声明一个迭代器，来访问vector容器，作用：遍历或者指向vector容器的元素
        for (it = TarPath.begin(); it != TarPath.end(); it++) {
            if (path != *it) {
                it = TarPath.erase(it);
                mark = true;    // 发生修改进行标记
            }
        }
    }

    // 释放 char** 的内存
    void FreeCString(char** destination, int size)
    {
        if (destination == nullptr) {
            return;
        }
        for (int n = 0; n < size; n++) {
            delete destination[n];
            destination[n] = nullptr;
        }
        destination = nullptr;
    }

    // 将 vector中的所有string 转为一个 char**
    void MakeCString(const std::vector < std::string >& source, char**& destination)
    {
        // 释放资源
        FreeCString(destination, static_cast < int > (source.size()));
        // 注意释放内存
        destination = new char *[static_cast < int > (source.size())];
        std::string desStr;
        size_t strLen;
        errno_t err = EOK;
        for (int n = 0; n < static_cast<int>(source.size()); ++n) {
            desStr = rootPath + "/" + source[n];
            strLen = desStr.size() + 1;
            destination[n] = new char[strLen];
            err = strcpy_s(destination[n], strLen, desStr.c_str());
            if (err != EOK) {
                continue;
            }
        }
    }

    char **GetTarPaths()
    {
        if (mark) { // 判断是否发生修改
            MakeCString(TarPath, cTarPath);
        }
        mark = false;   // 重置修改状态
        return cTarPath;
    }

    int GetTarPathCount()
    {
        return TarPath.size();
    }

    std::string getcommendPath()
    {
        std::string commend = "";
        std::vector < std::string >::iterator it;   // 声明一个迭代器，来访问vector容器，作用：遍历或者指向vector容器的元素
        for (it = TarPath.begin(); it != TarPath.end(); it++) {
            commend += "./" + (*it) + " ";
        }
        return commend;
    }

    void FreeTar()
    {
        if (cTarPath != nullptr) {
            FreeCString(cTarPath, static_cast < int > (TarPath.size()));
        }
        TarPath.clear();
        mark = true;
    }

private:
    std::string rootPath; // 不能清除
    std::vector<std::string> TarPath;
    char **cTarPath = nullptr;
    bool mark = true;
};


// 外部接口
static napi_value SetTarRootPath(napi_env env, napi_callback_info info)
{
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    // 把路径保存进去
    OHOStar::GetInstance().setTarRootPath(value0);
    set_rootPath(value0);

    napi_value ret;
    errno_t err = EOK;
    std::string desStr = "AddTarPath finish!";
    size_t strLen = desStr.size() + 1;
    char *retStr = new char[strLen];
    err = strcpy_s(retStr, strLen, desStr.c_str());
    if (err != EOK) {
        return NULL;
    }

    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));
    return ret;
}

static napi_value AddTarPath(napi_env env, napi_callback_info info)
{
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    // 把路径保存进去
    OHOStar::GetInstance().addTarpath(value0);

    napi_value ret;
    errno_t err = EOK;
    std::string desStr = "AddTarPath finish!";
    size_t strLen = desStr.size() + 1;
    char *retStr = new char[strLen];
    err = strcpy_s(retStr, strLen, desStr.c_str());
    if (err != EOK) {
        return NULL;
    }

    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));

    return ret;
}

static napi_value SiTar(napi_env env, napi_callback_info info)
{
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    napi_value ret;
    int res = -1;
    if (OHOStar::GetInstance().GetTarPathCount() <= 0) {
        NAPI_CALL(env, napi_create_int32(env, res, &ret));
        return ret;
    }
    
    res = Tar(value0, OHOStar::GetInstance().GetTarPaths(), OHOStar::GetInstance().GetTarPathCount());

    // 清除tar工具的实例记录
    OHOStar::GetInstance().FreeTar();

    NAPI_CALL(env, napi_create_int32(env, res, &ret));
    return ret;
}

static napi_value SiUnTar(napi_env env, napi_callback_info info)
{
    // 获取参数
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    char value1[MAX_STRING_LENGTH];
    size_t size1 = 0;
    napi_status unTargetToPath = napi_get_value_string_utf8(env, args[1], value1, MAX_STRING_LENGTH, &size1);

    char **unTarPath = new char *[1];
    unTarPath[0] = value1;

    napi_value ret;

    int res = UnTar(value0, unTarPath, 0);

    unTarPath = nullptr;

    // 清除tar工具的实例
    OHOStar::GetInstance().FreeTar();

    NAPI_CALL(env, napi_create_int32(env, res, &ret));
    return ret;
}

EXTERN_C_START

/*
 * function for module exports
 */
static napi_value Init(napi_env env, napi_value exports)
{
    /*
     * Properties define
     */
    napi_property_descriptor desc[] = {
        {.utf8name = "setTarRootPath",
         .name = nullptr,
         .method = SetTarRootPath,
         .getter = nullptr,
         .setter = nullptr,
         .value = nullptr,
         .attributes = napi_default,
         .data = nullptr},
        {.utf8name = "addTarPath",
         .name = nullptr,
         .method = AddTarPath,
         .getter = nullptr,
         .setter = nullptr,
         .value = nullptr,
         .attributes = napi_default,
         .data = nullptr},
        {.utf8name = "sitar",
         .name = nullptr,
         .method = SiTar,
         .getter = nullptr,
         .setter = nullptr,
         .value = nullptr,
         .attributes = napi_default,
         .data = nullptr},
        {.utf8name = "siuntar",
         .name = nullptr,
         .method = SiUnTar,
         .getter = nullptr,
         .setter = nullptr,
         .value = nullptr,
         .attributes = napi_default,
         .data = nullptr}};
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
    return exports;
}
EXTERN_C_END

/*
 * Module define
 */
static napi_module
tarPlugin = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "tar",
    .nm_priv = ((void *)0),
    .reserved = { 0 },
};

/*
 * Module register function
 */
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
    napi_module_register(& tarPlugin);
}
